import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

/**
 * Created by chenjunlai on 2017/11/27.
 */
public class HolidayRequest {
	public static ProcessEngine processEngine;
	Scanner scanner= new Scanner(System.in);

	@Before
	public void test00(){
		ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()//创建了一个独立的配置对象。这里的“独立”是指这样一个事实，即引擎是完全由自己创建和使用的（而不是在Spring环境中，而不是使用SpringProcessEngineConfiguration类）
				.setJdbcUrl("jdbc:mysql://localhost:3306/mzjpa")
				.setJdbcUsername("root")
				.setJdbcPassword("123456")
				.setJdbcDriver("com.mysql.jdbc.Driver")
				.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

		processEngine = cfg.buildProcessEngine();

	}


	/**
	 * 该应用程序运行没有问题，但是，没有有用的信息显示在控制台中，除了一个消息，指出日志记录没有正确配置：
	 */
	@Test
	public void test01(){
		/*ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()//创建了一个独立的配置对象。这里的“独立”是指这样一个事实，即引擎是完全由自己创建和使用的（而不是在Spring环境中，而不是使用SpringProcessEngineConfiguration类）
				.setJdbcUrl("jdbc:mysql://localhost:3306/mzjpa")
				.setJdbcUsername("root")
				.setJdbcPassword("123456")
				.setJdbcDriver("com.mysql.jdbc.Driver")
				.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

		ProcessEngine processEngine = cfg.buildProcessEngine();*/

		//创建一个新的部署

		RepositoryService repositoryService = processEngine.getRepositoryService();
		Deployment deploy = repositoryService.createDeployment().addClasspathResource("holiday-request.bpmn20.xml").deploy();

		// 现在，我们可以通过API查询流程定义来确认流程定义是否已经被引擎所了解（并学习了一些API）。
		// 这是通过RepositoryService创建一个新的ProcessDefinitionQuery对象来完成的。

		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
		System.out.println("Found process definition : " + processDefinition.getName());

		//要启动流程实例，我们需要提供一些初始流程变量。
		// 通常情况下，当某个进程被自动触发时，您将通过呈现给用户的表单或通过REST API获取这些表单。
		// 在这个例子中，我们将保持简单并使用java.util.Scanner类在命令行上简单地输入一些数据：

		System.out.println("Who are you?");
		String employee = "Jon";

		System.out.println("How many holidays do you want to request?");
		Integer nrOfHolidays = 7;

		System.out.println("Why do you need them?");
		String description = "休假";

		//接下来，我们可以通过RuntimeService启动一个流程实例。
		// 收集的数据以java.util.Map实例的形式传递，其中的关键字是稍后将用于检索变量的标识符。
		// 流程实例使用密钥启动。
		// 此键匹配在BPMN 2.0 XML文件中设置的id属性，在此示例中为holidayRequest(在文件holiday-request.bpmn20.xml[line14]中定义)。

		RuntimeService runtimeService = processEngine.getRuntimeService();

		Map<String, Object> variables = new HashMap<String, Object>();
		variables.put("employee", employee);
		variables.put("nrOfHolidays", nrOfHolidays);
		variables.put("description", description);
		ProcessInstance processInstance =
				runtimeService.startProcessInstanceByKey("holidayRequest", variables);

		System.out.println("processInstance Id 是：" + processInstance.getId());

		//当流程实例启动时，会创建一个执行并放入启动事件。从那里，这个执行遵循用户任务的顺序流程以供管理者批准，并执行用户任务行为。此行为将在数据库中创建一个任务，以后可以使用查询找到该任务。用户任务是等待状态，引擎将停止执行任何操作，返回API调用。

		//Sidetrack: transactionality
		//Sidetrack：事务性
		//在Flowable中，数据库事务对保证数据一致性和解决并发问题起着至关重要的作用。
		// 当您进行Flowable API调用时，默认情况下，所有内容都是同步的并且是同一事务的一部分。
		// 意思是，当方法调用返回时，事务将被启动并提交。

		//当一个流程实例启动时，从流程实例的开始到下一个等待状态将会有一个数据库事务。
		// 在这个例子中，这是第一个用户任务。
		// 当引擎到达这个用户任务时，状态被持久化到数据库，事务被提交并且API调用返回。

		//在Flowable中，当继续一个流程实例时，总是有一个数据库事务从前一个等待状态转到下一个等待状态。
		// 一旦持久化，数据可以在数据库中很长一段时间，甚至数年（如果必须的话），直到执行进一步采用流程实例的API调用。
		// 请注意，当流程实例处于等待状态时，不会消耗任何计算资源或内存资源，从而等待下一个API调用。

		//在这里的示例中，当第一个用户任务完成时，将使用一个数据库事务从用户任务通过专用网关（自动逻辑）直到第二个用户任务。
		// 或者直到另一条路(分支)的尽头。
	}

	/**
	 * 在执行此方法前,请重新初始化数据库,并且重新运行Test01
	 */
	@Test
	public void test02(){

		//为了得到实际的任务列表，我们通过TaskService创建一个TaskQuery，并且配置查询只返回管理员组的任务：
		TaskService taskService = processEngine.getTaskService();
		List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list();
		System.out.println("You have " + tasks.size() + " tasks:");
		for (int i=0; i<tasks.size(); i++) {
			System.out.println((i+1) + ") " + tasks.get(i).getName());
		}

		System.out.println("Which task would you like to complete?");
		int taskIndex = 1;//Integer.valueOf(scanner.nextLine());
		Task task = tasks.get(taskIndex - 1);
		Map<String, Object> processVariables = taskService.getVariables(task.getId());
		System.out.println(processVariables.get("employee") + " wants " +
				processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?");

		//经理现在可以完成任务。
		// 实际上，这通常意味着表单是由用户提交的。
		// 然后将表单中的数据作为流程变量传递。
		// 在这里，我们将通过在已完成的任务完成时向已批准的变量传递一个映射
		// （名称非常重要，因为稍后会在序列流的条件中使用！）：
		boolean approved = true;//scanner.nextLine().toLowerCase().equals("y");
		Map<String, Object> variables = new HashMap<String, Object>();
		variables = new HashMap<String, Object>();
		variables.put("approved", approved);
		taskService.complete(task.getId(), variables);

	}

	/**
	 * 使用历史数据
	 * 选择使用流程引擎（如Flowable）的众多原因之一是因为它会自动存储所有流程实例的审计数据或历史数据。
	 * 通过这些数据，您可以创建丰富的报告，深入了解组织的工作方式，瓶颈所在的位置等。
	 *
	 * 例如，假设我们想要显示迄今为止执行的流程实例的持续时间。
	 * 为此，我们 从ProcessEngine获取HistoryService，并为历史活动创建一个查询。
	 * 在下面的代码片段中，您可以看到我们添加了一些额外的过滤：
	 *
	 * 只有一个特定流程实例的活动
	 *
	 * 只有已完成的活动
	 *
	 * 结果也按结束时间排序，这意味着我们将按照执行顺序进行排序。
	 */
	@Test
	public void test03_UseHistory(){
		HistoryService historyService = processEngine.getHistoryService();
		List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
				.processDefinitionId("4")
				.finished()
				.orderByHistoricActivityInstanceEndTime()
				.asc()
				.list();
		for (HistoricActivityInstance activity : activities) {
			System.out.println(activity.getActivityId() + " took "
					+ activity.getDurationInMillis() + " milliseconds");
		}
	}
}
